package com.songlanyun.modules.minio.service;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.songlanyun.common.exception.RRException;
import com.songlanyun.modules.minio.config.MinioProperties;
import com.songlanyun.modules.minio.entity.UploadResult;
import com.songlanyun.modules.minio.enums.PolicyType;
import com.songlanyun.pay.common.util.AliPayManager;
import io.minio.*;
import io.minio.http.HttpUtils;
import okhttp3.HttpUrl;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;

/**
 * minio存储策略
 *
 * @author Lion Li
 */
//@Component
public class MinioService {

    private MinioClient minioClient;

    private MinioProperties minioProperties;

    public MinioService(MinioProperties minioProperties) {
        this.minioProperties = minioProperties;
        initMinioClient();
    }

    public void initMinioClient() {
        if (ObjectUtil.isNull(minioClient)) {
            try {
                MinioClient.Builder builder = MinioClient.builder();
                if (minioProperties.getIsHttps()) {
                    HttpUrl url = HttpUtils.getBaseUrl(minioProperties.getEndpoint())
                            .newBuilder().scheme("https").build();
                    builder.endpoint(url);
                } else {
                    builder.endpoint(minioProperties.getEndpoint());
                }
                minioClient = builder.credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey()).build();
                createBucket();
            } catch (Exception e) {
                throw new RRException("Minio存储配置错误! 请检查系统配置:[" + e.getMessage() + "]");
            }
        }

    }


    public void createBucket() {
        try {
            String bucketName = minioProperties.getBucketName();
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (exists) {
                return;
            }
            // 不存在就创建桶
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
                    .bucket(bucketName)
                    .config(getPolicy(bucketName, PolicyType.READ))
                    .build());
        } catch (Exception e) {
            throw new RRException("创建Bucket失败, 请核对Minio配置信息:[" + e.getMessage() + "]");
        }
    }


    public UploadResult upload(byte[] data, String path, String contentType) {
        return upload(new ByteArrayInputStream(data), path, contentType);
    }

    public UploadResult upload(InputStream inputStream, String path, String contentType) {
        try {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minioProperties.getBucketName())
                    .object(path)
                    .contentType(StrUtil.blankToDefault(contentType, MediaType.APPLICATION_OCTET_STREAM_VALUE))
                    .stream(inputStream, inputStream.available(), -1)
                    .build());
        } catch (Exception e) {
            throw new RRException("上传文件失败，请核对Minio配置信息:[" + e.getMessage() + "]");
        }
//        return UploadResult.builder().url(getEndpointLink() + "/" + path).filename(path).build();
        String[] split = path.split("/");
        String fileName = split[split.length - 1];
        return UploadResult.builder().url(getEndpointLink() + "/" + path).filename(fileName).build();
    }

    public void delete(String path) {
        path = path.replace(getEndpointLink() + "/", "");
        try {
            minioClient.removeObject(RemoveObjectArgs.builder()
                    .bucket(minioProperties.getBucketName())
                    .object(path)
                    .build());
        } catch (Exception e) {
            throw new RRException(e.getMessage());
        }
    }


    public UploadResult uploadSuffix(byte[] data, String suffix, String contentType) {
        return upload(data, getPath(minioProperties.getPrefix(), suffix), contentType);
    }


    public UploadResult uploadSuffix(InputStream inputStream, String suffix, String contentType) {
        return upload(inputStream, getPath(minioProperties.getPrefix(), suffix), contentType);
    }


    public String getEndpointLink() {
        return minioProperties.getEndpoint() + "/" + minioProperties.getBucketName();
    }

    private String getPolicy(String bucketName, PolicyType policyType) {
        StringBuilder builder = new StringBuilder();
        builder.append("{\n");
        builder.append("    \"Statement\": [\n");
        builder.append("        {\n");
        builder.append("            \"Action\": [\n");
        if (policyType == PolicyType.WRITE) {
            builder.append("                \"s3:GetBucketLocation\",\n");
            builder.append("                \"s3:ListBucketMultipartUploads\"\n");
        } else if (policyType == PolicyType.READ_WRITE) {
            builder.append("                \"s3:GetBucketLocation\",\n");
            builder.append("                \"s3:ListBucket\",\n");
            builder.append("                \"s3:ListBucketMultipartUploads\"\n");
        } else {
            builder.append("                \"s3:GetBucketLocation\"\n");
        }
        builder.append("            ],\n");
        builder.append("            \"Effect\": \"Allow\",\n");
        builder.append("            \"Principal\": \"*\",\n");
        builder.append("            \"Resource\": \"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("\"\n");
        builder.append("        },\n");
        if (PolicyType.READ.equals(policyType)) {
            builder.append("        {\n");
            builder.append("            \"Action\": [\n");
            builder.append("                \"s3:ListBucket\"\n");
            builder.append("            ],\n");
            builder.append("            \"Effect\": \"Deny\",\n");
            builder.append("            \"Principal\": \"*\",\n");
            builder.append("            \"Resource\": \"arn:aws:s3:::");
            builder.append(bucketName);
            builder.append("\"\n");
            builder.append("        },\n");
        }
        builder.append("        {\n");
        builder.append("            \"Action\": ");
        switch (policyType) {
            case WRITE:
                builder.append("[\n");
                builder.append("                \"s3:AbortMultipartUpload\",\n");
                builder.append("                \"s3:DeleteObject\",\n");
                builder.append("                \"s3:ListMultipartUploadParts\",\n");
                builder.append("                \"s3:PutObject\"\n");
                builder.append("            ],\n");
                break;
            case READ_WRITE:
                builder.append("[\n");
                builder.append("                \"s3:AbortMultipartUpload\",\n");
                builder.append("                \"s3:DeleteObject\",\n");
                builder.append("                \"s3:GetObject\",\n");
                builder.append("                \"s3:ListMultipartUploadParts\",\n");
                builder.append("                \"s3:PutObject\"\n");
                builder.append("            ],\n");
                break;
            default:
                builder.append("\"s3:GetObject\",\n");
                break;
        }
        builder.append("            \"Effect\": \"Allow\",\n");
        builder.append("            \"Principal\": \"*\",\n");
        builder.append("            \"Resource\": \"arn:aws:s3:::");
        builder.append(bucketName);
        builder.append("/*\"\n");
        builder.append("        }\n");
        builder.append("    ],\n");
        builder.append("    \"Version\": \"2012-10-17\"\n");
        builder.append("}\n");
        return builder.toString();
    }

    public String getPath(String prefix, String suffix) {
        // 生成uuid
        String uuid = IdUtil.fastSimpleUUID();
        // 文件路径
        String path = DateFormatUtils.format(new Date(), "yyyy/MM/dd") + "/" + uuid;
        if (StringUtils.isNotBlank(prefix)) {
            path = prefix + "/" + path;
        }
        return path + suffix;
    }

    public UploadResult upload(MultipartFile file) {
        String originalfileName = file.getOriginalFilename();
        String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());

        UploadResult uploadResult;
        try {
            uploadResult = this.uploadSuffix(file.getBytes(), suffix, file.getContentType());
        } catch (IOException e) {
            throw new RRException(e.getMessage());
        }

        return uploadResult;
    }
}
